//**************************************************
// Description: generate UDP frame
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module tx_UDP_generator #(
    parameter IN_DATA_WIDTH         = 128   ,
    parameter IN_MOD_WIDTH          = 4     
    ) ( 
    input                                               clk ,
    input                                               rst_n ,
    // frame info FIFO read
    output                                              frame_info_FIFO_rden ,
    input       [1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]    frame_info_FIFO_rdata ,
    // protocol generator interface
    output                                              UDP_generator_rdy ,
    input       [47:0]                                  DATALINK_dst_mac_fifo_i ,
    input       [47:0]                                  DATALINK_src_mac_fifo_i ,
    input       [31:0]                                  NETWORK_src_IP_fifo_i ,
    input       [31:0]                                  NETWORK_dst_IP_fifo_i ,
    input       [15:0]                                  TRANSPORT_src_port_fifo_i ,
    input       [15:0]                                  TRANSPORT_dst_port_fifo_i ,
    input       [10:0]                                  protocol_length ,
    input       [15:0]                                  protocol_CRC_checksum ,
    input                                               protocol_gen_finish ,
    // IP frame generator interface
    input                                               IP_gen_rdy ,
    output      [IN_DATA_WIDTH-1:0]                     protocol_gen_data ,
    output      [IN_MOD_WIDTH-1:0]                      protocol_gen_mod ,
    output                                              protocol_gen_sav ,
    output                                              protocol_gen_val ,
    output                                              protocol_gen_sop ,
    output                                              protocol_gen_eop ,
    output      [10:0]                                  protocol_gen_length ,
    output      [15:0]                                  protocol_gen_checksum ,
    output      [47:0]                                  DATALINK_dst_mac_fifo_o ,
    output      [47:0]                                  DATALINK_src_mac_fifo_o ,
    output      [31:0]                                  NETWORK_src_IP_fifo_o ,
    output      [31:0]                                  NETWORK_dst_IP_fifo_o 
) ;

    //----------------------------------------------
    // input signal store
    //----------------------------------------------
    // FIFO read delay
    reg                                             frame_info_FIFO_rden_r_ff1 ;
    reg     [1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]    frame_info_FIFO_rdata_ff1 ;

    //----------------------------------------------
    // output signal declare
    //----------------------------------------------
    // frame info FIFO read
    reg                                 frame_info_FIFO_rden_r ;
    // IP frame generator interface
    reg     [IN_DATA_WIDTH-1:0]         protocol_gen_data_r ;
    reg     [IN_MOD_WIDTH-1:0]          protocol_gen_mod_r ;
    reg                                 protocol_gen_sav_r ;
    reg                                 protocol_gen_val_r ;
    reg                                 protocol_gen_sop_r ;
    reg                                 protocol_gen_eop_r ;
    reg     [10:0]                      protocol_gen_length_r ;
    reg     [15:0]                      protocol_gen_checksum_r ;
    // frame info
    reg     [47:0]                      DATALINK_dst_mac_fifo_o_r ;
    reg     [47:0]                      DATALINK_src_mac_fifo_o_r ;
    reg     [31:0]                      NETWORK_src_IP_fifo_o_r ;
    reg     [31:0]                      NETWORK_dst_IP_fifo_o_r ;
    reg     [15:0]                      TRANSPORT_src_port_fifo_i_ff ;
    reg     [15:0]                      TRANSPORT_dst_port_fifo_i_ff ;


    // temp signal(be used to generate checksum)
    reg     [15:0]                      UDP_checksum_header ;
    wire    UDP_checksum_header_all_zero ;
    assign UDP_checksum_header_all_zero = !(|UDP_checksum_header) ;
    // temp signal(UDP fake header)
    reg     [95:0]                      UDP_fake_header ;
    // temp signal(be used to count fetched data)
    reg     [10:0]                      last_frame_length ;
    // temp signal(be used to stop fetch data)
    wire    get_frame_eop ;
    assign get_frame_eop = (last_frame_length==11'b0) ;
    // temp signal(be used to mark the first time to send)
    reg                                 first_get_frame ;


    // FIFO read enable delay
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            frame_info_FIFO_rden_r_ff1 <= 1'b0 ;
        else
            frame_info_FIFO_rden_r_ff1 <= frame_info_FIFO_rden_r ;
    end
    // delay FIFO data
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            frame_info_FIFO_rdata_ff1 <= {(1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0}} ;
        else if(frame_info_FIFO_rden_r_ff1)
            frame_info_FIFO_rdata_ff1 <= frame_info_FIFO_rdata ;
        else
            frame_info_FIFO_rdata_ff1 <= frame_info_FIFO_rdata_ff1 ;
    end


//**************************************************
// FSM 
//**************************************************
    localparam UDP_GEN_IDLE             = 3'd0 ;
    localparam UDP_GEN_FAKE_HEAD        = 3'd1 ;
    localparam UDP_GEN_CRC_GEN          = 3'd2 ;
    localparam UDP_GEN_GET_FRAME        = 3'd3 ;
    localparam UDP_GEN_SEND_FRAME       = 3'd4 ;
    localparam UDP_GEN_SEND_LAST1       = 3'd5 ;
    localparam UDP_GEN_SEND_LAST2       = 3'd6 ;
    localparam UDP_GEN_END              = 3'd7 ;

    reg     [2:0]                       udp_gen_state_c ;
    reg     [2:0]                       udp_gen_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            udp_gen_state_c <= UDP_GEN_IDLE ;
        else
            udp_gen_state_c <= udp_gen_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case (udp_gen_state_c)
            UDP_GEN_IDLE: begin
                if(protocol_gen_finish && IP_gen_rdy)
                    udp_gen_state_n = UDP_GEN_FAKE_HEAD ;
                else
                    udp_gen_state_n = UDP_GEN_IDLE ;
            end

            UDP_GEN_FAKE_HEAD: begin
                udp_gen_state_n = UDP_GEN_CRC_GEN ;
            end

            UDP_GEN_CRC_GEN: begin
                if(UDP_checksum_header_all_zero)
                    udp_gen_state_n = UDP_GEN_GET_FRAME ;
                else
                    udp_gen_state_n = UDP_GEN_CRC_GEN ;
            end

            UDP_GEN_GET_FRAME: begin
                udp_gen_state_n = UDP_GEN_SEND_FRAME ;
            end

            UDP_GEN_SEND_FRAME: begin
                if( get_frame_eop )
                    udp_gen_state_n = UDP_GEN_SEND_LAST1 ;
                else 
                    udp_gen_state_n = UDP_GEN_SEND_FRAME ;
            end

            UDP_GEN_SEND_LAST1: begin
                udp_gen_state_n = UDP_GEN_SEND_LAST2 ;
            end

            UDP_GEN_SEND_LAST2: begin
                udp_gen_state_n = UDP_GEN_END ;
            end

            UDP_GEN_END: begin
                udp_gen_state_n = UDP_GEN_IDLE ;
            end

            default: 
                udp_gen_state_n = UDP_GEN_IDLE ;
        endcase
    end
    // FSM(3)
    // fake header gen
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) 
            UDP_fake_header <= 96'b0 ; 
        else if(protocol_gen_finish)
            UDP_fake_header <= { NETWORK_src_IP_fifo_i, NETWORK_dst_IP_fifo_i, 8'b0, `NETWORK_UDP, {5'b0, (protocol_length+4'd8)} } ;
        else // only be used in "UDP_GEN_FAKE-HEAD" STATE
            UDP_fake_header <= 96'b0 ;
    end
    // UDP checksum
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            UDP_checksum_header     <= 16'b0 ;
            protocol_gen_checksum_r <= 16'b0 ;
        end
        else if( (udp_gen_state_c==UDP_GEN_FAKE_HEAD) ) begin
            {UDP_checksum_header, protocol_gen_checksum_r} <= UDP_fake_header[95:80] + UDP_fake_header[79:64] + 
                    + UDP_fake_header[63:48] + UDP_fake_header[47:32] + UDP_fake_header[31:16] + UDP_fake_header[15:0] + 
                    TRANSPORT_src_port_fifo_i_ff + TRANSPORT_dst_port_fifo_i_ff + UDP_fake_header[15:0] + 
                    protocol_CRC_checksum ;
        end
        else if( (udp_gen_state_c==UDP_GEN_CRC_GEN) ) begin
            {UDP_checksum_header, protocol_gen_checksum_r} <= UDP_checksum_header + protocol_gen_checksum_r ;
        end
        else begin
            UDP_checksum_header     <= UDP_checksum_header ;
            protocol_gen_checksum_r <= protocol_gen_checksum_r ;
        end
    end
    // first get frame 
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            first_get_frame <= 1'b1 ;
        else if( udp_gen_state_c==UDP_GEN_IDLE )
            first_get_frame <= 1'b1 ;
        else if( udp_gen_state_c==UDP_GEN_SEND_FRAME )
            first_get_frame <= 1'b0 ;
        else
            first_get_frame <= first_get_frame ;
    end
    // FIFO last data count
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            last_frame_length <= 11'b0 ;
        else if( protocol_gen_finish )
            last_frame_length <= protocol_length ;
        else if( ((udp_gen_state_c==UDP_GEN_CRC_GEN) && UDP_checksum_header_all_zero) ||
                    (udp_gen_state_c==UDP_GEN_GET_FRAME) || 
                    ((udp_gen_state_c==UDP_GEN_SEND_FRAME) && (!get_frame_eop)) )
            last_frame_length <= last_frame_length - 11'd16 ;
        else
            last_frame_length <= last_frame_length ;
    end
    // read frame info FIFO
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            frame_info_FIFO_rden_r <= 1'b0 ;
        else if( ((udp_gen_state_c==UDP_GEN_CRC_GEN) && UDP_checksum_header_all_zero) ||
                    (udp_gen_state_c==UDP_GEN_GET_FRAME) ||
                    ((udp_gen_state_c==UDP_GEN_SEND_FRAME) && (!get_frame_eop)) )
            frame_info_FIFO_rden_r <= 1'b1 ;
        else
            frame_info_FIFO_rden_r <= 1'b0 ;
    end
    // Local Link "data"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) 
            protocol_gen_data_r <= {(IN_DATA_WIDTH){1'b0}} ;
        else if(udp_gen_state_c==UDP_GEN_SEND_FRAME) begin
            if(first_get_frame)
                protocol_gen_data_r <= { TRANSPORT_src_port_fifo_i_ff, TRANSPORT_dst_port_fifo_i_ff, {5'b0, protocol_gen_length_r}, 
                                        ~(protocol_gen_checksum_r), frame_info_FIFO_rdata[(IN_DATA_WIDTH-1)-:64] } ;
            else
                protocol_gen_data_r <= { frame_info_FIFO_rdata_ff1[63:0], frame_info_FIFO_rdata[(IN_DATA_WIDTH-1)-:64] } ;
        end
        else if(udp_gen_state_c==UDP_GEN_SEND_LAST1)
            protocol_gen_data_r <= { frame_info_FIFO_rdata_ff1[63:0], frame_info_FIFO_rdata[(IN_DATA_WIDTH-1)-:64] } ;
        else if(udp_gen_state_c==UDP_GEN_SEND_LAST2) begin
            protocol_gen_data_r <= { frame_info_FIFO_rdata_ff1[63:0], 64'b0 } ;
        end
        else
            protocol_gen_data_r <= {(IN_DATA_WIDTH){1'b0}} ;
    end
    // "mod"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_mod_r <= {(IN_MOD_WIDTH){1'b0}} ;
        else if(udp_gen_state_c==UDP_GEN_SEND_LAST2)
            protocol_gen_mod_r <= (frame_info_FIFO_rdata_ff1[(IN_MOD_WIDTH+IN_DATA_WIDTH-1)-:IN_MOD_WIDTH]-8) ;
        else if((udp_gen_state_c==UDP_GEN_SEND_FRAME) || (udp_gen_state_c==UDP_GEN_SEND_LAST1))
            protocol_gen_mod_r <= {(IN_MOD_WIDTH){1'b0}} ;
        else
            protocol_gen_mod_r <= {(IN_MOD_WIDTH){1'b0}} ;
    end
    // "sav"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_sav_r <= 1'b0 ;
        else if(udp_gen_state_c==UDP_GEN_CRC_GEN)
            protocol_gen_sav_r <= 1'b1 ;
        else if(udp_gen_state_c==UDP_GEN_END)
            protocol_gen_sav_r <= 1'b0 ;
        else
            protocol_gen_sav_r <= protocol_gen_sav_r ;
    end
    // "val"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_val_r <= 1'b0 ;
        else if( (udp_gen_state_c==UDP_GEN_SEND_FRAME) || (udp_gen_state_c==UDP_GEN_SEND_LAST1) || (udp_gen_state_c==UDP_GEN_SEND_LAST2) )
            protocol_gen_val_r <= 1'b1 ;
        else 
            protocol_gen_val_r <= 1'b0 ;
    end
    // "sop"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_sop_r <= 1'b0 ;
        else if( (udp_gen_state_c==UDP_GEN_SEND_FRAME) && first_get_frame )
            protocol_gen_sop_r <= 1'b1 ;
        else 
            protocol_gen_sop_r <= 1'b0 ;
    end
    // "eop"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_eop_r <= 1'b0 ;
        else if(udp_gen_state_c==UDP_GEN_SEND_LAST2) 
            protocol_gen_eop_r <= 1'b1 ;
        else
            protocol_gen_eop_r <= 1'b0 ;
    end
    // "frame length"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_length_r <= 11'b0 ;
        else if(protocol_gen_finish) 
            protocol_gen_length_r <= (protocol_length+4'd8) ;
        else
            protocol_gen_length_r <= protocol_gen_length_r ;
    end



    // frame info store
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            DATALINK_dst_mac_fifo_o_r <= {(48){1'b0}} ;
            DATALINK_src_mac_fifo_o_r <= {(48){1'b0}} ;
            NETWORK_src_IP_fifo_o_r <= {(32){1'b0}} ;
            NETWORK_dst_IP_fifo_o_r <= {(32){1'b0}} ;
            TRANSPORT_src_port_fifo_i_ff <= {(16){1'b0}} ;
            TRANSPORT_dst_port_fifo_i_ff <= {(16){1'b0}} ;
        end
        else if(protocol_gen_finish) begin
            DATALINK_dst_mac_fifo_o_r <= DATALINK_dst_mac_fifo_i ;
            DATALINK_src_mac_fifo_o_r <= DATALINK_src_mac_fifo_i ;
            NETWORK_src_IP_fifo_o_r <= NETWORK_src_IP_fifo_i ;
            NETWORK_dst_IP_fifo_o_r <= NETWORK_dst_IP_fifo_i ;
            TRANSPORT_src_port_fifo_i_ff <= TRANSPORT_src_port_fifo_i ;
            TRANSPORT_dst_port_fifo_i_ff <= TRANSPORT_dst_port_fifo_i ;
        end
        else begin
            DATALINK_dst_mac_fifo_o_r <= DATALINK_dst_mac_fifo_o_r ;
            DATALINK_src_mac_fifo_o_r <= DATALINK_src_mac_fifo_o_r ;
            NETWORK_src_IP_fifo_o_r <= NETWORK_src_IP_fifo_o_r ;
            NETWORK_dst_IP_fifo_o_r <= NETWORK_dst_IP_fifo_o_r ;
            TRANSPORT_src_port_fifo_i_ff <= TRANSPORT_src_port_fifo_i_ff ;
            TRANSPORT_dst_port_fifo_i_ff <= TRANSPORT_dst_port_fifo_i_ff ;
        end
    end



//**************************************************
// output assign
//**************************************************
    assign UDP_generator_rdy        = (udp_gen_state_c==UDP_GEN_IDLE)?1'b1:1'b0 ;
    assign frame_info_FIFO_rden     = frame_info_FIFO_rden_r ;
    assign protocol_gen_data        = protocol_gen_data_r ;
    assign protocol_gen_mod         = protocol_gen_mod_r ;
    assign protocol_gen_sav         = protocol_gen_sav_r ;
    assign protocol_gen_val         = protocol_gen_val_r ;
    assign protocol_gen_sop         = protocol_gen_sop_r ;
    assign protocol_gen_eop         = protocol_gen_eop_r ;
    assign protocol_gen_length      = protocol_gen_length_r ;
    assign protocol_gen_checksum    = protocol_gen_checksum_r ;
    assign DATALINK_dst_mac_fifo_o  = DATALINK_dst_mac_fifo_o_r ;
    assign DATALINK_src_mac_fifo_o  = DATALINK_src_mac_fifo_o_r ;
    assign NETWORK_src_IP_fifo_o    = NETWORK_src_IP_fifo_o_r ;
    assign NETWORK_dst_IP_fifo_o    = NETWORK_dst_IP_fifo_o_r ;

endmodule
